package com.fakeDYChat.common.user.service.impl;

import com.fakeDYChat.common.common.annotation.RedissonLock;
import com.fakeDYChat.common.common.event.UserBlackEvent;
import com.fakeDYChat.common.common.event.UserRegisterEvent;
import com.fakeDYChat.common.common.utils.AssertUtil;
import com.fakeDYChat.common.user.dao.BlackDao;
import com.fakeDYChat.common.user.dao.ItemConfigDao;
import com.fakeDYChat.common.user.dao.UserBackpackDao;
import com.fakeDYChat.common.user.dao.UserDao;
import com.fakeDYChat.common.user.domain.entity.*;
import com.fakeDYChat.common.user.domain.enums.BlackTypeEnum;
import com.fakeDYChat.common.user.domain.enums.ItemEnum;
import com.fakeDYChat.common.user.domain.enums.ItemTypeEnum;
import com.fakeDYChat.common.user.domain.vo.resp.BadgeResp;
import com.fakeDYChat.common.user.domain.vo.resp.BlackResp;
import com.fakeDYChat.common.user.domain.vo.resp.UserInfoResp;
import com.fakeDYChat.common.user.domain.vo.resp.WearingBadgeReq;
import com.fakeDYChat.common.user.mapper.UserMapper;
import com.fakeDYChat.common.user.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fakeDYChat.common.user.service.adapter.UserAdapter;
import com.fakeDYChat.common.user.service.cache.ItemCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Tang
 * @since 2023-11-18
 */
@Service
@Slf4j
public class IUserImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    private final UserDao userDao;
    private final UserBackpackDao userBackpackDao;
    private final ItemCache itemCache;
    private final ItemConfigDao itemConfigDao;
    private final ApplicationEventPublisher applicationEventPublisher;
    private final BlackDao blackDao;

    public IUserImpl(UserDao userDao, UserBackpackDao userBackpackDao,
                     ItemCache itemCache, ItemConfigDao itemConfigDao,
                     ApplicationEventPublisher applicationEventPublisher,
                     BlackDao blackDao) {
        this.userDao = userDao;
        this.userBackpackDao = userBackpackDao;
        this.itemCache = itemCache;
        this.itemConfigDao = itemConfigDao;
        this.applicationEventPublisher = applicationEventPublisher;
        this.blackDao = blackDao;
    }

    @Override
    @Transactional
    public Long register(User user) {
        userDao.save(user);
        //发送用户注册事件
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, user));
        return user.getId();
    }

    /**
     * 获取用户个人信息
     *
     * @param uid 用户id
     * @return com.fakeDYChat.common.user.domain.vo.resp.UserInfoResp
     * @author Tang
     * @date 2023/12/30 15:32
     **/
    @Override
    public UserInfoResp getUserInfo(Long uid) {
        User user = userDao.getById(uid);
        Integer modifyCount = userBackpackDao.selectCountByValidItemId(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        return UserAdapter.buildUserInfo(user, modifyCount);
    }

    /**
     * 用户修改个人名称
     *
     * @param uid     用户id
     * @param newName 新用户名
     * @author Tang
     * @date 2023/12/30 16:54
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    @RedissonLock(key = "#uid")
    public void modifyName(Long uid, String newName) {
        //查询该名称是否被占用
        User user = userDao.getByName(newName);
        AssertUtil.isEmpty(user, "名字重复了，请换一个吧");
        //查询是否还有可用的改名卡
        UserBackpack modifyCard = userBackpackDao.getFirstValidItem(uid, ItemEnum.MODIFY_NAME_CARD.getId());
        AssertUtil.isNotEmpty(modifyCard, "卡片用完了，请购买或等待后续的活动");
        //使用乐观锁的方式，使用背包中的物品
        Boolean success = userBackpackDao.useItem(modifyCard);
        if (success) {
            userDao.modifyName(uid, newName);
        }
    }

    /**
     * 展示用户徽章列表
     *
     * @param uid 用户id
     * @return java.util.List<com.fakeDYChat.common.user.domain.vo.resp.BadgeResp>
     * @author Tang
     * @date 2024/1/2 15:20
     **/
    @Override
    public List<BadgeResp> badges(Long uid) {
        //查询所有徽章
        List<ItemConfig> allBadges = itemCache.getByType(ItemTypeEnum.BADGE.getType());
        //查询用户拥有的徽章
        List<UserBackpack> userOwnByItemIds = userBackpackDao
                .getUserOwnByItemIds(uid, allBadges.stream().map(ItemConfig::getId).collect(Collectors.toList()));
        //查询用户佩戴的徽章
        User user = userDao.getById(uid);
        //包装返回信息
        return UserAdapter.buildBadgeResp(allBadges, userOwnByItemIds, user);
    }

    /**
     * 用户佩戴徽章
     *
     * @param uid 用户id
     * @param req 需要佩戴的徽章信息
     * @author Tang
     * @date 2024/1/2 19:58
     **/
    @Override
    public void wearingBadge(Long uid, WearingBadgeReq req) {
        //查询这个物品是否为徽章
        ItemConfig itemConfig = itemConfigDao.getById(req.getItemId());
        AssertUtil.equal(itemConfig.getType(), ItemTypeEnum.BADGE.getType(), "此物品不是徽章，不能佩戴哦");
        //查询用户是否拥有该物品
        UserBackpack firstValidItem = userBackpackDao.getFirstValidItem(uid, req.getItemId());
        AssertUtil.isNotEmpty(firstValidItem, "你还没有这个徽章哦，快去获得吧");
        //佩戴徽章
        userDao.wearingBadge(uid, req.getItemId());
    }

    /**
     * 拉黑用户
     *
     * @param req 拉黑信息
     * @author Tang
     * @date 2024/1/12 20:14
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void black(BlackResp req) {
        Long uid = req.getUid();
        //根据uid拉黑
        blackUid(uid);
        //根据ip拉黑
        User byId = userDao.getById(uid);
        blackIp(Optional.ofNullable(byId.getIpInfo()).map(IpInfo::getUpdateIp).orElse(""));
        applicationEventPublisher.publishEvent(new UserBlackEvent(this, byId));
    }

    /**
     * 根据ip封禁
     *
     * @param ip 封禁用户的ip
     * @author Tang
     * @date 2024/1/12 20:21
     **/
    private void blackIp(String ip) {
        try {
            if (StringUtils.isBlank(ip)) {
                return;
            }
            Black user = new Black();
            user.setType(BlackTypeEnum.IP.getType());
            user.setTarget(ip);
            blackDao.save(user);
        } catch (Exception e) {
            log.error(ip + "，该ip已拉黑过");
        }
    }

    /**
     * 根据uid封禁用户
     *
     * @param uid 用户id
     * @author Tang
     * @date 2024/1/12 20:20
     **/
    private void blackUid(Long uid) {
        Black user = new Black();
        user.setType(BlackTypeEnum.UID.getType());
        user.setTarget(uid.toString());
        blackDao.save(user);
    }
}
